/***********************************
 *  MessageOps is an activity with two major features:
 *   this is where SMS messages are decrypted if necessary, and displayed to the user
 *   this is also where a user may encrypt a new message, or reply to an existing message 
 */
package edu.csuohio.mad.ssmk.subtxt;

import java.io.InputStream;
import android.app.Activity;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.telephony.PhoneNumberUtils;
import android.telephony.SmsManager;
import android.telephony.TelephonyManager;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.View;
import android.view.Window;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class MessageOps extends Activity {

	private static final int PICK_CONTACT = 101;
	private Uri uriSMSInbox = Uri.parse("content://sms/inbox");
	private static final String[] PROJECTION = new String[] { "_id", "address",
			"body", "date" };
	private CipherSupport cipher = CipherSupport.getInstance();
	private ContactSupport cs = ContactSupport.getInstance(this);
	private String number;
	private TextView tvSize;
	private boolean isEncrypted = true;

	// the text watcher allows us to provide a realtime number of characters
	// remaining
	private final TextWatcher tew = new TextWatcher() {
		public void beforeTextChanged(CharSequence s, int start, int count,
				int after) {
			tvSize = (TextView) findViewById(R.id.tvSize);
		}

		// when the text is changed, set the new number of remaining characters
		public void onTextChanged(CharSequence s, int start, int before,
				int count) {
			tvSize.setText(String.valueOf(150 - s.length()));
		}

		public void afterTextChanged(Editable s) {
		}
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		requestWindowFeature(Window.FEATURE_NO_TITLE);
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_mops);

		// use the telephony manager to get our device's phone number
		TelephonyManager tm = (TelephonyManager) this
				.getSystemService(Context.TELEPHONY_SERVICE);
		cipher.setKeySender(tm.getLine1Number());

		// add the text watcher to the message body field
		((EditText) findViewById(R.id.etBody)).addTextChangedListener(tew);

		// Load the current selected SMS if it exists
		Intent intent = getIntent();
		Bundle bundle = intent.getExtras();
		if (bundle != null) {
			long smsID = bundle.getLong("_id");
			// get the sms from the inbox and put it in the view
			readSMS(smsID);
			// mark it as read in the android inbox
			ContentValues values = new ContentValues();
			values.put("read", true);
			getContentResolver().update(Uri.parse("content://sms/"), values,
					"_id=" + smsID, null);
		}
	}

	@Override
	public void onActivityResult(int reqCode, int resultCode, Intent data) {
		super.onActivityResult(reqCode, resultCode, data);

		switch (reqCode) {
		case (PICK_CONTACT):
			// if the user picks a contact for a new message, we need to get
			// their details
			if (resultCode == Activity.RESULT_OK) {
				Uri contactData = data.getData();
				Cursor c = getContentResolver().query(contactData, null, null,
						null, null);
				if (c.moveToFirst()) {
					String name = c
							.getString(c
									.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));

					// set the recipient's name using the display name if
					// available
					TextView tvRecip = (TextView) findViewById(R.id.tvParty);
					tvRecip.setText(name);

					String id = c
							.getString(c
									.getColumnIndexOrThrow(ContactsContract.Contacts._ID));

					Cursor phoneCur = getContentResolver().query(
							ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
							null,
							ContactsContract.CommonDataKinds.Phone.CONTACT_ID
									+ " = ?", new String[] { id }, null);

					// put their phone number on the screen
					if (phoneCur.moveToFirst()) {
						String prettyNumber = phoneCur
								.getString(phoneCur
										.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
						phoneCur.close();
						c.close();
						TextView tvNumber = (TextView) findViewById(R.id.tvDetails);
						tvNumber.setText(prettyNumber);
						// also, strip any special characters out of the number
						// otherwise smsManager will be extremely dissatisfied
						number = PhoneNumberUtils.stripSeparators(prettyNumber);
					} else {
						number = PhoneNumberUtils.stripSeparators(number);
					}
				}
			}
			break;
		}
	}

	// spawn the system's choose contact activity
	public void chooseContact(View v) {
		Intent intent = new Intent(Intent.ACTION_PICK,
				ContactsContract.Contacts.CONTENT_URI);
		startActivityForResult(intent, PICK_CONTACT);
	}

	// encrypt the message body with our phone numbers and return the ciphertext
	public String encryptSMS(String plaintext, String number) {
		cipher.setKeyReceiver(number);
		cipher.setPlainText(plaintext);
		cipher.encrypt();
		return cipher.getCipherText();
	}

	// decrypt the message and return the plaintext
	public String decryptSMS(String ciphertext, String number) {
		cipher.setKeyReceiver(number);
		cipher.setCipherText(ciphertext);
		cipher.decrypt();
		return cipher.getPlainText();
	}

	// send an SMS! yay
	public void sendSMS(View v) {
		EditText etBody = (EditText) findViewById(R.id.etBody);
		String smsBody = etBody.getText().toString();

		TextView tvNumber = (TextView) findViewById(R.id.tvDetails);
		// make sure our number is clean, for some reason,
		// orientation changes screw this value up, don't ask me why
		number = PhoneNumberUtils
				.stripSeparators(tvNumber.getText().toString());

		// right, this should never happen, exactly
		if ((number != null)
				&& (!PhoneNumberUtils.isWellFormedSmsAddress(number))) {
			Toast.makeText(this, "Problem With Phone Number!!",
					Toast.LENGTH_LONG).show();
			return;
		}

		// if the user is sending a new message, or responding to an encrypted
		// message
		// lets encrypt the current plaintext
		if (isEncrypted)
			smsBody = "subtxt:" + encryptSMS(smsBody, number);

		// send the message using the sms manager
		try {
			SmsManager smsManager = SmsManager.getDefault();
			smsManager.sendTextMessage(number, null, smsBody, null, null);
		} catch (Exception e) {
			e.printStackTrace();
			Toast.makeText(this, "Message Failed!", Toast.LENGTH_LONG).show();
		}
		Toast.makeText(this, "Message Sent!", Toast.LENGTH_LONG).show();
		finish();
	}

	// read an sms, again, yay!
	private void readSMS(long id) {

		TextView tvParty = (TextView) findViewById(R.id.tvParty);
		TextView tvDetails = (TextView) findViewById(R.id.tvDetails);
		TextView tvBody = (TextView) findViewById(R.id.tvBody);
		tvBody.setVisibility(TextView.VISIBLE);

		// use the content resolver to get all the messages from the inbox
		Cursor cur = getContentResolver().query(uriSMSInbox, PROJECTION, null,
				null, null);
		// loop through the cursor until we get the one we want
		// i'm not sure why we're not updating the uri and going directly to it
		// but at this point i'm tired and it can be fixed in a future iteration
		while (cur.moveToNext()) {
			if (Long.parseLong(cur.getString(cur.getColumnIndex("_id"))) == id) {
				// get the number of the message
				number = cur.getString(cur.getColumnIndex("address"));

				// create the contact in our helper class, most likely they're
				// already in it as the message would have been listed in the
				// list activity
				cs.createContact(number);
				if (cs.hasContact(number)) {
					// set their display name
					tvParty.setText(cs.getDisplayName(number));

					// if they have an image uri, lets load that too
					if (cs.getImageUri(number) != null) {
						InputStream input = ContactsContract.Contacts
								.openContactPhotoInputStream(
										getContentResolver(),
										cs.getImageUri(number));
						ImageView iv = (ImageView) findViewById(R.id.sms_pic);
						iv.setImageBitmap(BitmapFactory.decodeStream(input));
					}

					// if by some fluke they're not in the hashmap, set the text
					// straight up
				} else {
					tvParty.setText(number);
				}
				// reformat the timestamp and display it in the details
				tvDetails.setText(ContactSupport.reformatDate(cur.getString(cur
						.getColumnIndex("date"))));
				//set the ciphertext to the current body
				String ciphertext = cur.getString(cur.getColumnIndex("body"));
				// if the ciphertext contains our header, lets decrypt it
				if (ciphertext.startsWith("subtxt:")) {
					tvBody.setText(decryptSMS(ciphertext.substring(7), number));
					isEncrypted = true;
					// otherwise, lets just display it for all to see
				} else {
					tvBody.setText(ciphertext);
					isEncrypted = false;
				}
			}
		}
		cur.close();
	}
}